#pragma once

class AxisAlignedBox
{
public:
	AxisAlignedBox() : mIsValid(false) {}
	AxisAlignedBox(const Vector3& min, const Vector3& max) : mMin(min), mMax(max), mIsValid(true) {}
	AxisAlignedBox(float minx, float miny, float minz, float maxx, float maxy, float maxz) 
		: mMin(minx, miny, minz), mMax(maxx, maxy, maxz), mIsValid(true) {}

	const Vector3& getMin() const { return mMin; }
	const Vector3& getMax() const { return mMax; }
	Vector3 getCenter() const { return (mMin + mMax) * 0.5f; }
	Vector3 getSize() const { return mMax - mMin; }
	Vector3 getHalfSize() const { return (mMax - mMin) * 0.5f; }

	void Init();
	void Init(const Vector3& MIN, const Vector3& MAX);
	void Merge(const AxisAlignedBox& box);
	void Merge(const Vector3& point);
	void TransformAffine(const Matrix& m);
	bool Valid() const { return mIsValid; }

	bool OverlapTest(const AxisAlignedBox& other, float tol = 0) const;

private:
	// Variables.
	Vector3 mMin;
	Vector3 mMax;
	bool mIsValid;
};

inline void AxisAlignedBox::Init()
{
	mMin = mMax = Vector3(0, 0, 0);
	mIsValid = false;
}

inline void AxisAlignedBox::Init(const Vector3& MIN, const Vector3& MAX)
{
	mMin = MIN;
	mMax = MAX;
	mIsValid = true;
}

inline void AxisAlignedBox::Merge(const AxisAlignedBox& box)
{
	if (!box.mIsValid)
		return;

	if (!mIsValid)
	{
		mMin = box.mMin;
		mMax = box.mMax;
	}
	else
	{
		mMin.MakeFloor(box.mMin);
		mMax.MakeCeil(box.mMax);
	}
	mIsValid = true;
}

inline void AxisAlignedBox::Merge(const Vector3& point)
{
	if (!mIsValid)
	{
		mMin = point;
		mMax = point;
	}
	else
	{
		mMin.MakeFloor(point);
		mMax.MakeCeil(point);
	}

	mIsValid = true;
}

inline void AxisAlignedBox::TransformAffine(const Matrix& m)
{
	if (!mIsValid)
		return;

	Vector3 centre = getCenter();
	Vector3 halfSize = getHalfSize();

	Vector3 newCentre = m.TransformAffine(centre);
	Vector3 newHalfSize(
		Abs(m[0][0]) * halfSize.X + Abs(m[1][0]) * halfSize.Y + Abs(m[2][0]) * halfSize.Z, 
		Abs(m[0][1]) * halfSize.X + Abs(m[1][1]) * halfSize.Y + Abs(m[2][1]) * halfSize.Z,
		Abs(m[0][2]) * halfSize.X + Abs(m[1][2]) * halfSize.Y + Abs(m[2][2]) * halfSize.Z);

	mMin = newCentre - newHalfSize;
	mMax = newCentre + newHalfSize;
}

inline bool AxisAlignedBox::OverlapTest(const AxisAlignedBox& other, float tol) const
{
	return ((mMin.Z >= other.mMax.Z + tol) ||
		(mMax.Z <= other.mMin.Z - tol) ||
		(mMin.Y >= other.mMax.Y + tol) ||
		(mMax.Y <= other.mMin.Y - tol) ||
		(mMin.X >= other.mMax.X + tol) ||
		(mMax.X <= other.mMin.X - tol)) ? false : true;
}
